package edu.mapi.ir.viewer;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Vector;

import javax.swing.JFrame;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import edu.mapi.ir.gui.GuiUtils;
import edu.mapi.ir.planning.SimpleMap;

/**
 * @author ZP
 */
public class Maze implements IMapLayer {

	private Vector<Wall> walls = new Vector<Wall>();
	private Vector<GridPosition> grid = new Vector<GridPosition>();
	private double width = 7.5, height = 7.5;
	private String name = "Unnamed maze";
	private Beacon beacon = new Beacon();
	protected BufferedImage backgroundTile = GuiUtils.toBufferedImage(GuiUtils.getImage("imgs/background.png"));
	protected BufferedImage wallTile = GuiUtils.toBufferedImage(GuiUtils.getImage("imgs/brick.png"));
	protected BufferedImage grassTile = GuiUtils.toBufferedImage(GuiUtils.getImage("imgs/grass.png"));
	protected Image cookieImg = GuiUtils.getImage("imgs/cookie.png");
	
	public static Maze readStream(InputStream stream) throws Exception {
		
		Maze maze = new Maze();
		
		DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = fac.newDocumentBuilder();
		Document doc = db.parse(stream);
		
		Node root = doc.getChildNodes().item(0);
		String name = root.getAttributes().getNamedItem("Name").getTextContent();
		String width = root.getAttributes().getNamedItem("Width").getTextContent();
		String height = root.getAttributes().getNamedItem("Height").getTextContent();
		
		maze.setHeight(Double.parseDouble(height));
		maze.setWidth(Double.parseDouble(width));
		maze.setName(name);
		
		NodeList wallNodes = doc.getElementsByTagName("Wall");
		
		
		for (int i = 0; i < wallNodes.getLength(); i++) {
						
			String wheight = wallNodes.item(i).getAttributes().getNamedItem("Height").getTextContent();
			Wall wall = new Wall(Double.parseDouble(wheight));
			NodeList cornerNodes = wallNodes.item(i).getChildNodes();
			for (int j = 0; j < cornerNodes.getLength(); j++) {
				
				if (!cornerNodes.item(j).getNodeName().equals("Corner")) {
					continue;
				}
				
				String xstr = cornerNodes.item(j).getAttributes().getNamedItem("X").getTextContent();
				String ystr = cornerNodes.item(j).getAttributes().getNamedItem("Y").getTextContent();
				
				wall.addCorner(new Point2D.Double(Double.parseDouble(xstr),Double.parseDouble(ystr)));
								
			}
			maze.walls.add(wall);
		}
		
		
		Node beaconNode = doc.getElementsByTagName("Beacon").item(0);
		Node targetNode = doc.getElementsByTagName("Target").item(0);
		
		Beacon beacon = new Beacon();
		beacon.setHeight(Double.parseDouble(beaconNode.getAttributes().getNamedItem("Height").getTextContent()));
		beacon.setRadius(Double.parseDouble(targetNode.getAttributes().getNamedItem("Radius").getTextContent()));
		String xpos = beaconNode.getAttributes().getNamedItem("X").getTextContent();
		String ypos = beaconNode.getAttributes().getNamedItem("Y").getTextContent();

		String txpos = targetNode.getAttributes().getNamedItem("X").getTextContent();
		String typos = targetNode.getAttributes().getNamedItem("Y").getTextContent();

		beacon.setPosition(new Point2D.Double(Double.parseDouble(xpos), Double.parseDouble(ypos)));
		beacon.setTargetCenter(new Point2D.Double(Double.parseDouble(txpos), Double.parseDouble(typos)));
		maze.setBeacon(beacon);
		//System.out.println(beacon.toString());
		return maze;		
	}
	
	public static Maze readString(String xml) throws Exception {
		return readStream(new ByteArrayInputStream(xml.getBytes()));
	}
	
	public static Maze readFile(File f) throws Exception {
		return readStream(new FileInputStream(f));
	}
	
	public static Maze readXML(File mazeFile, File gridFile) throws Exception {
		Maze maze = readFile(mazeFile);		
		Vector<GridPosition> grid = GridPosition.readFile(gridFile);
		maze.setGrid(grid);
			
		return maze;
	}
	
	public Vector<Wall> getWalls() {
		return walls;
	}
	public void setWalls(Vector<Wall> walls) {
		this.walls = walls;
	}
	public double getWidth() {
		return width;
	}
	public void setWidth(double width) {
		this.width = width;
	}
	public double getHeight() {
		return height;
	}
	public void setHeight(double height) {
		this.height = height;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Beacon getBeacon() {
		return beacon;
	}
	public void setBeacon(Beacon beacon) {
		this.beacon = beacon;
	}
	
	@Override
	public String toString() {
		String ret = "<Lab Name=\""+getName()+"\" Width=\""+getWidth()+"\" Height=\""+getHeight()+"\">\n";
		ret += beacon.toString();
		for (Wall w : walls) {
			ret += w.toString();
		}
		ret += "</Lab>\n";
		return ret;
	}
	
	
	
	
	public BufferedImage getLayerImage(double zoom) {
		BufferedImage img = new BufferedImage((int)(getWidth() * zoom),(int) (getHeight() * zoom), BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = (Graphics2D)img.getGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setColor(new Color(130,130,100));
		
		if (backgroundTile != null) {
			TexturePaint tpaint = new TexturePaint(backgroundTile, new Rectangle2D.Double(0,0,backgroundTile.getWidth(null), backgroundTile.getHeight(null)));
			g.setPaint(tpaint);
		}
		else
			g.setColor(Color.gray);
		
		g.fillRect(0, 0, (int) (getWidth()*zoom), (int) (getHeight()*zoom));
		g.scale(zoom, -zoom);		
		g.translate(0, -getHeight());
		
		g.setColor(new Color(255,0,0,50));
		g.fill(beacon.getShape());
		g.setColor(new Color(255,255,255,70));
		g.setStroke(new BasicStroke((float)(2.0/zoom)));		
		g.draw(beacon.getShape());
		AffineTransform tmp = g.getTransform();
		
		g.setTransform(new AffineTransform());
		g.translate(beacon.getPosition().getX() * zoom, (getHeight() - beacon.getPosition().getY())*zoom);
		
		if (zoom >= 40)
			g.drawImage(cookieImg, -25, -25, null);
		else if (zoom >= 20) {
			g.scale(0.5, 0.5);
			g.drawImage(cookieImg, -25, -25, null);
		}
		else if (zoom >= 10){
			g.scale(0.25, 0.25);
			g.drawImage(cookieImg, -25, -25, null);
		}
		else {
			g.setColor(new Color(255,255,0));
			g.draw(new Line2D.Double(-3, -3, 3, 3));
			g.draw(new Line2D.Double(-3, 3, 3, -3));
		}

		
				
		
		g.setTransform(tmp);
		
		TexturePaint wallPaint = new TexturePaint(wallTile, new Rectangle2D.Double(0,0,wallTile.getWidth(null)/zoom, wallTile.getHeight(null)/zoom));
		
		AffineTransform previousTransform = g.getTransform(); 
		
		Area highWalls = new Area();
		Area lowWalls = new Area();
		
		for (Wall w : walls) {
			if (w.getHeight() <= beacon.getHeight())
				lowWalls.add(new Area(w.getShape()));			
			else 
				highWalls.add(new Area(w.getShape()));				
		}

		g.setColor(new Color(0,0,0,100));	
		g.translate(5/zoom, -5/zoom);
		g.fill(highWalls);
		g.setTransform(previousTransform);
		
		// LOW WALLS
		// paint shadow
		g.setColor(new Color(0,0,0,100));					
		g.translate(2/zoom, -2/zoom);
		g.fill(lowWalls);
		g.setTransform(previousTransform);
		//paint brick texture
		//g.setPaint(grassPaint);	
		//g.fill(lowWalls);
		//paint border
		g.setColor(Color.black);
		g.setStroke(new BasicStroke((float)(1/zoom)));		
		g.draw(lowWalls);
		// color
		g.setColor(new Color(250,250,250,100));				
		g.fill(lowWalls);
		// HIGH WALLS
		// shadow
		g.setColor(new Color(0,0,0,100));	
		for (int i = 0; i < 3; i++) {
			g.translate(1/zoom, -1/zoom);
			g.fill(highWalls);
		}		
		g.setTransform(previousTransform);
		// brick paint
		if (wallPaint != null)
			g.setPaint(wallPaint);
		else
			g.setColor(new Color(200, 100, 50));
		g.fill(highWalls);
		// border
		g.setColor(Color.black);
		g.setStroke(new BasicStroke((float)(1.5/zoom)));
		g.draw(highWalls);
		// color
		//g.setColor(new Color(200, 180, 0, 150));
		//g.fill(highWalls);
			
		g.setColor(new Color(150,150,150,100));		
		for (GridPosition pos : grid) {
			g.fill(pos.getShape());
		}
		
		g.setColor(Color.black);
		g.setStroke(new BasicStroke((float)(2.0/zoom)));
		g.setTransform(new AffineTransform());
		g.drawRect(0, 0, (int) (getWidth()*zoom)-1, (int) (getHeight()*zoom)-1);		
		return img;
	}
	
	public static void main(String[] args) throws Exception {
		final Maze m = readXML(new File("c:/maze21.xml"), new File("c:/grid.xml"));
		JFrame frame = new JFrame("Lab Viewer");

		MazePanel mp = new MazePanel();
		mp.setMaze(m);
		mp.addLayer(new BeaconVisibility(m, m.getBeacon()));		
		mp.addLayer(new ChessLayer(m));
		
		SimpleMap sm = new SimpleMap(m);		
		
		mp.addLayer(sm);
				
		frame.getContentPane().add(mp);
		frame.setSize(800, 650);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
				
	}

	public Vector<GridPosition> getGrid() {
		return grid;
	}

	public void setGrid(Vector<GridPosition> grid) {
		this.grid = grid;
	}
	
	public Area getObstacleArea() {
		Area a = new Area();
		for (Wall w : walls) {
			a.add(new Area(w.getShape()));
		}
		return a;
	}
	
	public boolean hasLayerChanged() {
		return false;
	}
}
